#ifndef CQ_LOOPER_H
#define CQ_LOOPER_H

#include "CQ_defines.h"
#include "CQ_task_queue.h"

#include <map>
#include <thread>
#include <atomic>
#include <algorithm>

DEFINE_NAMESPACE_CQ_BEGIN

class CQLooper;
inline std::map<std::thread::id, CQLooper*>& gLoopers() { static std::map<std::thread::id, CQLooper*> m_loopers; return m_loopers; }
inline std::mutex& gMutex() { static std::mutex m_mutex; return m_mutex; }

enum LOOPER_TYPE
{
    SINGLE,
    REPEAT
};

class CQLooper
{
public:
	static CQLooper* currentLooper()
	{
		return getLooper(std::this_thread::get_id());
	}

	static CQLooper* getLooper(const std::thread::id& id_)
	{
		std::unique_lock<std::mutex> _lock(gMutex());
		auto _looper = gLoopers().find(id_);
		CQLooper* _re = nullptr;
		if (_looper != gLoopers().end()) {
			_re = (*_looper).second;
		}
		else {
			_re = new CQLooper();
			gLoopers()[id_] = _re;
		}
		return _re;
	}

	void enqueue(CQTaskBase* t_)
	{
		return m_queue.enqueue(t_);
	}

	bool dequeue(CQTaskBase*& t_, int ms_ = -1)
	{
		return m_queue.dequeue(t_, ms_);
	}

	void exec_once(int ms_ = -1)
	{
		CQTaskBase* _task = nullptr;
		do {
			if (m_queue.dequeue(_task, ms_)) {
				if (_task->type() == TASK_COMMON) {
					_task->run();
					delete _task;
					break;
				}

				if (_task->type() == TASK_DELAY) {
					if (_task->ttl() == 0) {
						_task->run();
						delete _task;
					}
					else {
						m_queue.enqueue(_task);
					}
				}
			}
		} while (0);
	}

	int exec(int ms_ = -1)
	{
		int _re = 1;
		while (m_isRunning) {
			CQTaskBase* _task = nullptr;
			if (m_queue.dequeue(_task, ms_)) {
				if (_task->type() == TASK_COMMON) {
					_task->run();
					delete _task;
					continue;
				}

				if (_task->type() == TASK_DELAY) {
					if (_task->ttl() == 0) {
						_task->run();
						delete _task;
						continue;
					}
					else {
						m_queue.enqueue(_task);
					}
				}
			}
			waitdelay();
		}
		return _re;
	}

	void exit()
	{
		m_isRunning = false;
	}

private:
	CQLooper()
		: m_isRunning(true)
	{

	}

	void waitdelay()
	{
		std::unique_lock<std::mutex> _lock(m_queue.m_mutex);
		auto ite = std::min_element(m_queue.m_queue.begin(), m_queue.m_queue.end(),
			[](const CQTaskBase* t1, const CQTaskBase* t2) { return t1->ttl() < t2->ttl(); });
		if (ite != m_queue.m_queue.end() && (*ite)->ttl()) {
			m_queue.m_cv.wait_until(_lock, std::chrono::system_clock::now() + std::chrono::milliseconds((*ite)->ttl()));
		}
	}

	CQTaskQueue m_queue;
	std::atomic_bool m_isRunning;
};

DEFINE_NAMESPACE_CQ_END

#define CQ_EXEC() CQSLOT::CQLooper::currentLooper()->exec()

#endif // CQ_LOOPER_H
